//
//  NSObject+Runtime.m
//  runtime
//
//  Created by yang on 2018/1/8.
//  Copyright © 2018年 YYY. All rights reserved.
//

#import "NSObject+Runtime.h"

#import <objc/runtime.h>

@implementation NSObject (Runtime)

static const char *propertiesKey = "propertiesKey";
//void *propertiesKey = "propertiesKey";
//获取类的属性列表
+ (NSArray *)propertiesList
{
    unsigned int count = 0;
    objc_property_t *list = class_copyPropertyList([self class], &count);
    //遍历所有的属性
    NSMutableArray *arrayM = [NSMutableArray array];
    for (unsigned int i = 0; i < count; i++) {
        objc_property_t pty = list[i];
        //获取 属性 名称
        const char *cName = property_getName(pty);
        NSString *name = [NSString stringWithUTF8String:cName];
        [arrayM addObject:name];
    }
    free(list);
    //设置关联对象
    objc_setAssociatedObject(self, propertiesKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);
    return objc_getAssociatedObject(self, propertiesKey);
}

static const char *methodsKey = "methodsKey";
//获取类的方法列表
+ (NSArray *)methodsList
{
    unsigned int count = 0;
    Method *list = class_copyMethodList([self class], &count);
    //遍历所有的属性
    NSMutableArray *arrayM = [NSMutableArray array];
    for (unsigned int i=0; i<count; i++)
    {
        Method method = list[i];
        SEL selector = method_getName(method);
        NSString *methodName = NSStringFromSelector(selector);
        [arrayM addObject:methodName];
    }
    free(list);
    //设置关联对象
    objc_setAssociatedObject(self, methodsKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);
    return objc_getAssociatedObject(self, methodsKey);;
}

static const char *ivarsKey = "ivarsKey";
//void *ivarsKey = "ivarsKey";
//获取类的ivar成员变量列表
+ (NSArray *)ivarsList
{
    unsigned int count = 0;
    Ivar *list = class_copyIvarList([self class], &count);
    //遍历所有的属性
    NSMutableArray *arrayM = [NSMutableArray array];
    for (unsigned int i = 0; i < count; i++)
    {
        Ivar ivar = list[i];
        // 获取 ivar 名称
        const char *cName = ivar_getName(ivar);
        // 获取 ivar 类型
        //const char *cType = ivar_getTypeEncoding(ivar);
        NSString *name = [NSString stringWithUTF8String:cName];
        //NSString *type = [NSString stringWithUTF8String:cType];
        [arrayM addObject: name];
    }
    free(list);
    //设置关联对象
    objc_setAssociatedObject(self, ivarsKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);
    return objc_getAssociatedObject(self, ivarsKey);
}

//获取协议列表
static const char *protocolKey = "protocolKey";
+ (NSArray *)protocolList
{
    unsigned int count = 0;
    __unsafe_unretained Protocol **list = class_copyProtocolList([self class], &count);
    //遍历所有的属性
    NSMutableArray *arrayM = [NSMutableArray array];
    for (unsigned int i = 0; i < count; i++)
    {
        Protocol *protocol = list[i];
        //获取协议名称
        const char *cName = protocol_getName(protocol);
        NSString *name = [NSString stringWithUTF8String:cName];
        [arrayM addObject:name];
    }
    free(list);
    //设置关联对象
    objc_setAssociatedObject(self, protocolKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);
    return objc_getAssociatedObject(self, protocolKey);
}

static const char *nameKey = "nameKey";
//void *nameKey = "nameKey";
- (NSString *)name
{
    return objc_getAssociatedObject(self, nameKey);
}

- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

//字典转模型
+ (instancetype)objectWithDict:(NSDictionary *)dict
{
    // 创建对应模型对象
    id objc = [[self alloc] init];
    unsigned int count = 0;
    // 1.获取成员属性数组
    Ivar *ivarList = class_copyIvarList(self, &count);
    // 2.遍历所有的成员属性名,一个一个去字典中取出对应的value给模型属性赋值
    for (int i = 0; i < count; i++)
    {
        // 2.1 获取成员属性
        Ivar ivar = ivarList[i];
        // 2.2 获取成员属性名 C -> OC 字符串
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        // 2.3 _成员属性名 => 字典key
        NSString *key = [ivarName substringFromIndex:1];
        // 2.4 去字典中取出对应value给模型属性赋值
        id value = dict[key];
        // 获取成员属性类型
        NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        // 二级转换,字典中还有字典,也需要把对应字典转换成模型
        // 判断下value,是不是字典
        if ([value isKindOfClass:[NSDictionary class]] && ![ivarType containsString:@"NS"]) { //  是字典对象,并且属性名对应类型是自定义类型
            // user User
            // 处理类型字符串 @\"User\" -> User
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
            // 自定义对象,并且值是字典
            // value:user字典 -> User模型
            // 获取模型(user)类对象
            Class modalClass = NSClassFromString(ivarType);
            // 字典转模型
            if (modalClass)
            {
                // 字典转模型 user
                value = [modalClass objectWithDict:value];
            }
        }
        // 三级转换：NSArray中也是字典，把数组中的字典转换成模型.
        // 判断值是否是数组
        if ([value isKindOfClass:[NSArray class]])
        {
            // 判断对应类有没有实现字典数组转模型数组的协议
            if ([self respondsToSelector:@selector(arrayContainModelClass)])
            {
                // 转换成id类型，就能调用任何对象的方法
                id idSelf = self;
                // 获取数组中字典对应的模型
                NSString *type =  [idSelf arrayContainModelClass][key];
                // 生成模型
                Class classModel = NSClassFromString(type);
                NSMutableArray *arrM = [NSMutableArray array];
                // 遍历字典数组，生成模型数组
                for (NSDictionary *dict in value)
                {
                    // 字典转模型
                    id model =  [classModel objectWithDict:dict];
                    [arrM addObject:model];
                }
                // 把模型数组赋值给value
                value = arrM;
            }
        }
        // 2.5 KVC字典转模型
        if (value)
        {
            [objc setValue:value forKey:key];
        }
    }
    // 返回对象
    return objc;
}


@end
